//   Copyright [2008] [Joel Riedesel]
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.

import java.awt.*;


/**
 * TableLayout is similar to GridLayout except that each row and column
 * is sized independently based on the maximum height/width of the components
 * in the row/column.
 * 
 */

public class TableLayout implements LayoutManager {

  static final public int TOP    = 0;
  static final public int CENTER = 1;
  static final public int BOTTOM = 2;
  static final public int LEFT   = 3;
  static final public int RIGHT  = 4;

  static private int defaultGap = 5;

  private int rowcount;
  private int columns;
  private int horzAlignment;
  private int vertAlignment;
  private int horzGap;
  private int vertGap;
  private int heightV[];
  private int widthV[];
  private boolean fitToContainerWidthP = false;
  private int fitToContainerColumn = -1;

  // Initialize with rows = 0 in order to have TableLayout calculate the
  // number of rows dynamically from the number of columns and the
  // number of components.

  public TableLayout(int rows, int cols) {
    this(rows, cols, defaultGap, defaultGap, CENTER, CENTER);
  }

  public TableLayout(int rows, int cols, int hGap, int vGap) {
    this(rows, cols, hGap, vGap, CENTER, CENTER);
  }

  public TableLayout(int rows, int cols, int hGap, int vGap, int hAlign, int vAlign) {
//    Assert.notFalse(rows >= 0);
//    Assert.notFalse(cols > 0);
//    Assert.notFalse(hGap >= 0);
//    Assert.notFalse(vGap >= 0);
//    Assert.notFalse(hAlign == LEFT || hAlign == CENTER || hAlign == RIGHT);
//    Assert.notFalse(vAlign == TOP  || vAlign == CENTER || vAlign == BOTTOM);

    this.rowcount      = rows;
    this.columns       = cols;
    this.horzGap       = hGap;
    this.vertGap       = vGap;
    this.horzAlignment = hAlign;
    this.vertAlignment = vAlign;

    if(rows == 0) rows = 32;

    heightV = new int[rows];
    widthV  = new int[cols];
  }

  public void setSpacing(int hGap, int vGap) {
    this.horzGap = hGap;
    this.vertGap = vGap;
  }

  public void setAlignment(int hAlign, int vAlign) {
    this.horzAlignment = hAlign;
    this.vertAlignment = vAlign;
  }
  
  
  // If fitToContainerWidth is true, the column widths are adjusted
  // so that the overall table size fits the container width.
  public void setFitToContainerWidth(boolean fitP) {
    fitToContainerWidthP = fitP;
  }
  
  // If fitToContainerColumn is -1, all columns are stretched proportionally.
  // If fitToContainerColumn is a valid column number, only the indicated
  // column is stretched in order to fit the container size.
  public void setFitToContainerColumn(int column) {
    fitToContainerColumn = column;
  }
  
  public void addLayoutComponent(String name, Component comp) {
  }
  
  public void removeLayoutComponent(Component comp) { 
  }

  int getRowCount(int componentCount) {
    if(rowcount > 0) {
      return(rowcount);
    } else {
      int rows = (componentCount + (columns - 1)) / columns;
      if(heightV.length < rows) {
        heightV = new int[rows * 2];
      }
      return(rows);
    }
  }
  
  public int getAverageRowHeight(Container target) {
    Dimension d = preferredLayoutSize(target);
    int ccount = target.getComponentCount();
    int rows = getRowCount(ccount);
    return(d.height / rows);
  }


  public Dimension preferredLayoutSize(Container target) {
    int ccount = target.getComponentCount();
    int rows   = getRowCount(ccount);
    int r, c;
      
    for(r = 0; r < rows; r++) {
      heightV[r] = 0;
    }
    for(c = 0; c < columns; c++) {
      widthV[c] = 0;
    }

    for(r = 0; r < rows; r++) {
      for(c = 0; c < columns; c++) {
        int i = (r * columns) + c;
        if(i >= ccount) continue;

        Component comp = target.getComponent(i);
        if(comp.isVisible()) {
          Dimension d = comp.getPreferredSize();
 
          if(d.height > heightV[r]) {
            heightV[r] = d.height;
          }
          if(d.width > widthV[c]) {
            widthV[c] = d.width;
          }
        }
      }
    }

    int height = vertGap * (rows - 1);
    int width  = horzGap * (columns - 1);

    for(r = 0; r < rows; r++) {
      height += heightV[r];
    }
    for(c = 0; c < columns; c++) {
      width += widthV[c];
    }
    
    Insets insets = target.getInsets();
    Dimension dresult = new Dimension(width + insets.left + insets.right, 
                                      height + insets.top  + insets.bottom);
    return(dresult);
  }


  public Dimension minimumLayoutSize(Container target) {
    int ccount = target.getComponentCount();
    int rows   = getRowCount(ccount);
    int r, c;

    for(r = 0; r < rows; r++) {
      heightV[r] = 0;
    }
    for(c = 0; c < columns; c++) {
      widthV[c] = 0;
    }

    for(r = 0; r < rows; r++) {
      for(c = 0; c < columns; c++) {
        int i = (r * columns) + c;
        if(i >= ccount) continue;

        Component comp = target.getComponent(i);
        if(comp.isVisible()) {
          Dimension d = comp.getMinimumSize();
 
          if(d.height > heightV[r]) {
            heightV[r] = d.height;
          }
          if(d.width > widthV[c]) {
            widthV[c] = d.width;
          }
        }
      }
    }

    int height = vertGap * (rows - 1);
    int width  = horzGap * (columns - 1);

    for(r = 0; r < rows; r++) {
      height += heightV[r];
    }
    for(c = 0; c < columns; c++) {
      width += widthV[c];
    }

    Insets insets = target.getInsets();
    return new Dimension(width + insets.left + insets.right, 
                         height + insets.top  + insets.bottom);
  }


  public void layoutContainer(Container target) {
    preferredLayoutSize(target);

    Insets insets = target.getInsets();
    int    top    = insets.top;
    int    ccount = target.getComponentCount();
    int    rows   = getRowCount(ccount);
    int    r, c;
    double widthRatio = 1.0;
    int    stretchColWidth = 0;

    if(fitToContainerWidthP) {
      int gapWidth = horzGap * (columns - 1);
      int eltWidth = 0;
      for(c = 0; c < columns; c++) {
        eltWidth += widthV[c];
      }
      int twidth = target.getWidth() - gapWidth - insets.left - insets.right;
      if(fitToContainerColumn >= 0) {
        stretchColWidth = twidth - eltWidth + widthV[fitToContainerColumn];
      } else if(eltWidth > 0 && twidth > 0) {
        widthRatio = (double)twidth/(double)eltWidth; 
      }
    }
    
    for(r = 0; r < rows; r++) {
      int left = insets.left;

      for(c = 0; c < columns; c++) {
        int i = (r * columns) + c;
        if(i >= ccount) continue;

        int colWidth = widthV[c];
        if(fitToContainerWidthP) {
          if(fitToContainerColumn >= 0) {
            if(c == fitToContainerColumn) {
              colWidth = stretchColWidth;              
            }
          } else {
            colWidth = (int)Math.round(colWidth * widthRatio);
          }
        }
        
        Component comp = target.getComponent(i);
        if(comp.isVisible()) {
          Dimension ps = comp.getPreferredSize();
          int xoffset = 0;
          int yoffset = 0;
          int width   = ps.width;
          int height  = ps.height;

          if(horzAlignment == CENTER) {
            width = colWidth;
          } else if(horzAlignment == RIGHT) {
            xoffset = colWidth - ps.width;
          }
          if(vertAlignment == CENTER) {
            yoffset = (heightV[r] - ps.height) / 2;
          } else if (vertAlignment == BOTTOM) {
            yoffset = heightV[r] - ps.height;
          }
          comp.setBounds(left + xoffset, top + yoffset, width, height);
        }
        left += colWidth + horzGap;
      }
      top += heightV[r] + vertGap;
    }
  }


}
